#! /usr/bin/env python

import socket
from contextlib import closing


VERSION='0.1'
APPNAME='resource'

srcdir = '.'
blddir = 'build'

import os, sys, subprocess, socket
import http, urllib, time, atexit, urllib.error, urllib.request
from waflib import Logs
import waflib.Options
sys.path = ["src"] + sys.path # in order to import waf_resource
import waf_dynamo, waf_ddf, waf_resource
import TestContext

test_context = None

os.environ['PATH'] = os.path.abspath('src') + os.pathsep + os.environ['PATH']

def find_free_port():
    with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
        s.bind(('', 0))
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        return s.getsockname()[1]

def init(ctx):
    global test_context
    if 'build' in waflib.Options.commands:
        test_context = TestContext.create_test_context()

def _is_mac_ci():
    is_ci = os.environ.get('GITHUB_WORKFLOW') or os.environ.get('GITHUB_ACTIONS') or os.environ.get('CI')
    return is_ci and sys.platform == 'darwin'

def options(opt):
    opt.load('waf_dynamo')

def configure(conf):
    conf.load('waf_dynamo')
    conf.load('waf_resource')
    conf.load('java')

    if _is_mac_ci():
        conf.env.append_unique('DEFINES', 'GITHUB_CI')

    waf_ddf.configure(conf)

    # For some reason, the ranged server doesn't work on Win32
    if 'win32' == sys.platform:
        conf.env.append_value('DEFINES', 'DM_TEST_NO_SUPPORT_RANGE_REQUEST')

    conf.recurse('src')

    conf.env.append_value('INCLUDES', "src")
    conf.env['STLIB_DLIB'] = ['dlib', 'mbedtls', 'zip']
    conf.env['STLIB_DDF'] = 'ddf'

    conf.env.append_unique('DEFINES', 'DLIB_LOG_DOMAIN="RESOURCE"')

def build(bld):
    global test_context
    bld.recurse('src')
    TestContext.initialize_test_context(test_context, bld)

def kill_proc(proc):
    proc.kill()

def _get_local_ip(self):
    hostname = socket.gethostname()
    try:
        local_ip = socket.gethostbyname(hostname)
    except socket.gaierror as e:
        print(e)
        print(f"Hostname was '{hostname}', now trying empty ('') string now")
        local_ip = socket.gethostbyname("")
    return local_ip

def _start_server(self, local_ip, port, configname=None):
    Logs.info('Starting test server at http://%s:%d' % (local_ip, port))
    configfile = waf_dynamo.create_test_server_config(self, port=port, ip=local_ip, config_name=configname)

    # For some reason, the ranged server doesn't work on Win32
    servertype = "RangeHTTPServer"
    if 'win32' == sys.platform:
        servertype = "http.server"

    proc = subprocess.Popen(["python", "-m", servertype, str(port)],
                            shell = False,
                            cwd = "build/src/test")
    return proc, configfile

def _wait_for_server(self, proc, local_ip, port):
    timeout = 8 # seconds
    start = time.time()
    while True:
        if time.time() - start > timeout:
            Logs.error('HTTP server failed to start within %d seconds' % timeout)
            sys.exit(1)
        try:
            urllib.request.urlopen('http://%s:%d' % (local_ip, port))
            break
        except urllib.error.URLError:
            print('Waiting for HTTP testserver to start...')
            sys.stdout.flush()
            time.sleep(0.5)
        except Exception as e:
            self.fatal('Failed to start test server at http://%s:%d: %s: %s, Is there another test server running?' % (local_ip, port, type(e), e))


def _cleanup_server(args):
    proc, configpath = args
    kill_proc(proc)
    if os.path.exists(configpath):
        os.remove(configpath)

def server(self):
    local_ip = _get_local_ip(self)
    port = find_free_port()
    proc, configfile = _start_server(self, local_ip, port, "unittest.cfg")
    atexit.register(_cleanup_server, (proc, configfile))

    _wait_for_server(self, proc, local_ip, port)

    timeout = 240 # seconds
    start = time.time()
    while True:
        if time.time() - start > timeout:
            print('Shutting down server after %d seconds' % timeout)
            return
        time.sleep(1.0)

def shutdown(self):
    if not TestContext.is_valid(test_context) or waflib.Options.options.skip_build_tests or waflib.Options.options.skip_tests:
        return

    skip_server = _is_mac_ci()
    if skip_server:
        print("Running resource tests without HTTP server on macOS CI")

    configfile = None
    if not skip_server:
        local_ip = _get_local_ip(self)
        port = find_free_port()
        proc, configfile = _start_server(self, local_ip, port)
        atexit.register(_cleanup_server, (proc, configfile))

        _wait_for_server(self, proc, local_ip, port)
    else:
        local_ip = _get_local_ip(self)
        configfile = waf_dynamo.create_test_server_config(self, port=0, ip=local_ip, config_name="unittest.cfg")

    try:
        waf_dynamo.run_tests(test_context, valgrind = True, configfile = configfile)
    finally:
        if configfile and os.path.exists(configfile):
            os.remove(configfile)
